#include "httpd.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include "common.h"
#include "wrapsock.h"
#include "http_request.h"
#include "http_respond.h"
#include "user_controller.h"
#include "common_controller.h"

extern char default_page[];


/* URL与处理函数之间的映射*/
struct mapping
{
	char *url; /* 请求的URL */
	void (*controller)(struct http_request *request, struct http_respond *respond);   /* URL对应处理函数 */
};

/* 控制器注册数组 */
static struct mapping request_mapping[] = 
{
	{ "/user/login", login },
	{ "/user/join", siginup },
	{ "/user/loginpage", loginpage },
	{ "/user/joinpage", joinpage },
	{ "/user/captcha", get_captcha },

	{ "/init/index", init_index_page },
	{ "/init/join", init_join_page },
	{ NULL, NULL }
};


/* 应答状态 */
enum respond_status_type
{
	OK  = 200,
	NOTFOUND = 404
};
struct respond_status
{
	int error_code;   /* 错误码 */
	char *error_message;  /* 错误信息 */
};
static struct respond_status respond[] = {
    /* 请求已成功，请求所希望的响应头或数据体将随此响应返回 */
	{ OK, "HTTP/1.1 200 OK\r\n" },

    /* 1、语义有误，当前请求无法被服务器理解。除非进行修改，否则客户端不应该重复提交这个请求, 2、请求参数有误*/
    { 400, "HTTP/1.1 400 Bad Request\r\n" },

    /* 服务器已经理解请求，但是拒绝执行它 */
    { 403, "HTTP/1.1 403 Forbidden\r\n" },

    /* 请求失败，请求所希望得到的资源未被在服务器上发现 */
	{ NOTFOUND, "HTTP/1.1 404 Not Found\r\n" },

    /* 服务器拒绝在没有定义 Content-Length 头的情况下接受请求 */
    { 411, "HTTP/1.1 411 Length Required\r\n" },

    /* 服务器拒绝处理当前请求，因为该请求提交的实体数据大小超过了服务器愿意或者能够处理的范围  */
    { 413, "HTTP/1.1 413 Request Entity Too Large\r\n" },

    /* 请求的URI 长度超过了服务器能够解释的长度，因此服务器拒绝对该请求提供服务 */
    { 414, "HTTP/1.1 414 Request-URI Too Large\r\n" },

    /* 服务器遇到了一个未曾预料的状况，导致了它无法完成对请求的处理  */
    { 500, "HTTP/1.1 500 Internal Server Error\r\n" },

    /* 作为网关或者代理工作的服务器尝试执行请求时，从上游服务器接收到无效的响应 */
    { 502, "HTTP/1.1 502 Bad Gateway\r\n" },

    /* 数组结束 */
	{ -1, NULL }
};

/* content-type 下标 */
#define XWWWFORM        0
#define JAVASCRIPT      1
#define JSON            2
#define STREAM          3
#define PNG             4
#define JPG             5
#define XICON           6
#define FORMDATA        7
#define PLAIN           8
#define HTML            9
#define CSS             10
#define XML				11

/* content-type */
struct kv_map
{
    char *suffix;
    int value;
};


static char *content_type[] =
{
	[XWWWFORM] = 	"Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n\r\n",
	[JAVASCRIPT] = 	"Content-Type: application/javascript; charset=utf-8\r\n\r\n",
	[JSON] = 		"Content-Type: application/json; charset=utf-8\r\n\r\n",
	[STREAM] =		"Content-Type: application/octet-stream\r\n\r\n",
	[PNG] = 		"Content-Type: image/png\r\n\r\n",
	[XICON] = 		"Content-Type: image/x-icon\r\n\r\n",
	[JPG] = 		"Content-Type: image/jpeg\r\n\r\n",
	[FORMDATA] = 	"Content-Type: multipart/form-data\r\n\r\n",
	[PLAIN] = 		"Content-Type: text/plain; charset=utf-8\r\n\r\n",
	[HTML] = 		"Content-Type: text/html; charset=utf-8\r\n\r\n",
	[CSS] = 		"Content-Type: text/css; charset=utf-8\r\n\r\n",
	[XML] = 		"Content-Type: text/xml; charset=utf-8\r\n\r\n",
};

static struct kv_map content_type_suffix[] = 
{
    { ".js", JAVASCRIPT },
    { ".min.js", JAVASCRIPT },
    { ".css", CSS },
    { ".min.css", CSS },
    { ".html", HTML },
    { ".htm", HTML },
    { ".png", PNG },
    { ".jpg", JPG },
    { ".jpeg", JPG },
    { ".xml", XML },
    { ".json", JSON },
    { NULL, -1 }
};

/* 发送响应状态 */
static int
send_respond_error_code(int fd, int error_code)
{
	/* Get the error code corresponding information */
	char *message = NULL;
	struct respond_status *status = NULL;
	for (status = respond; status != NULL; status++)
	{
		if (status->error_code == error_code)
		{
			message = status->error_message;
			break;
		}
	}
	
	if (message == NULL)
	{
        send_respond_error_code(fd, 500);
		return (-1);
	}

	/* send to client */
    if (send(fd, message, strlen(message), 0) < 0)
    {
        printf("send error: %s", strerror(errno));
		return (-1);
    }
	return (0);
} 

/* 发送Content-Type */
static int
send_respond_content_type(int fd, int content_index)
{
    int length;
    length = strlen(content_type[content_index]);
    if (send(fd, content_type[content_index], length, 0) < 0)
    {
        printf("send error: %s", strerror(errno));
        return (-1);
    }
    return (0);
}

/* 根据请求的文件名决定发送的Content-Type类型 */
static int 
send_content_type_by_suffix(int fd, char *path)
{
	if (path == NULL)
	{
		return (-1);
	}

	char *pos = NULL;
	if ((pos = strchr(path, '.')) == NULL)
	{
	    return (send_respond_content_type(fd, PLAIN));
	}

    struct kv_map *content_type = NULL;
    content_type = content_type_suffix;
    while (content_type != NULL && content_type->suffix != NULL)
    {
        if (strncmp(pos, content_type->suffix, strlen(content_type->suffix)) == 0)
        {
		    return (send_respond_content_type(fd, content_type->value));
        }
        content_type++;
    }

	return (send_respond_content_type(fd, PLAIN));
}

/* 发送请求的文件 */
static int
send_respond_file(int fd, char *path)
{
    FILE *fp = NULL;
    char buff[MAXLINE];

	char *anchor = NULL;
	if ((anchor = strchr(path, '#')) != NULL)
	{
		*anchor  = '\0';
	}
    if (NULL != (fp = fopen(path, "r")))
    {
		send_respond_error_code(fd, OK);
		send_content_type_by_suffix(fd, path);

         int read_length; 
        while (0 != (read_length = fread(buff, 1, sizeof(buff), fp)))
        {
            if (send(fd, buff, read_length, 0) < 0)
            {
                printf("send error: %s", strerror(errno));
                break;
            }
        }
		return (0);
    }
    printf("can't open file: %s\n", path);
	send_respond_error_code(fd, 404);
	return (-1);
}

/* 发送应答数据 */
static void
send_respond_data(int fd, const void *respond_data, int size)
{
	if (respond_data != NULL)
	{
		if (send(fd, respond_data, size, 0) < 0)
		{
			printf("send error: %s\n", strerror(errno));
		}
	}
}

/* 处理接受到的数据 */
static void
handle_data(int accept_fd, struct http_request *request)
{
    if (request == NULL || strlen(request->path) == 0)
    {
        send_respond_error_code(accept_fd, 500);
        return ;
    }

    /* 遍历控制器 */
    struct http_respond *respond = NULL;
	struct mapping *map = NULL;
	map = request_mapping;
	while (map != NULL && map->url != NULL)
	{
		if (strcmp(map->url, request->path) == 0)
		{
            if ((respond = create_http_respond()) == NULL)
            {
                break;
            }

			map->controller(request, respond);
			break;
		}
		map++;
	}

    /* 没有找到对应的控制器时，默认为请求文件 */
	if (map == NULL || map->url == NULL || respond == NULL)
	{
        char file_path[512];
        if (strcmp(request->path, "/") == 0)
        {
            snprintf(file_path, sizeof(file_path), "%s", default_page);
        }
        else
        {
            snprintf(file_path, sizeof(file_path), "%s", request->path);
        }
        send_respond_file(accept_fd, file_path);
	}
	else  /* 控制器返回信息 */
	{
        if (respond->redirect != NULL)
        {
           if (send_respond_file(accept_fd, respond->redirect) < 0)
           {
                return ;
           }
        }
        else
        {
	        send_respond_error_code(accept_fd, OK);
            send_respond_content_type(accept_fd, PLAIN);
        }

        printf("respond_data: %s\n", (char *)respond->data);
		send_respond_data(accept_fd, respond->data, respond->data_length);
        destroy_http_respond(respond);
	}
	fflush(NULL);
}


static void
resolve_data(int accept_fd, char *recv_data)
{
    struct http_request *request = NULL;
    if ((request  = string_to_http_requeset(recv_data)) == NULL)
    {
        send_respond_error_code(accept_fd, 500);
        return ;
    }

	printf("method = %s, path = %s, version = %s, host = %s"
        ", content-type = %s, charset = %s\n",
        request->method, request->path, request->version, request->host,
        request->content_type, request->charset);

    handle_data(accept_fd, request);
    destroy_http_request(request);
}

void
handle_request(int accept_fd)
{
	/* that will be have two accept_fd socket,if success */
	pid_t pid;
	pid = fork();
	switch (pid)
	{
	case -1:
		printf("fork error: %s\n", strerror(errno));
		break;
	case 0: // second child
       { 
		//7. receive messsage
        ssize_t n;
		char buff[MAXLINE * 100];
		memset(buff, 0, sizeof(buff));

		if ((n = recv_timeout(accept_fd, buff, sizeof(buff))) > 0)
		{
			fprintf(stdout, "recv: %s\n", buff);
			resolve_data(accept_fd, buff);
            close(accept_fd);
            exit(0);
		}
		printf("recv error: %s\n", strerror(errno));
        }
		break;
	default:
		/*
		 *first child, only keep the listen_fd of the parent process
		 */
		close(accept_fd);

		/*
		 * make the parent process of the second
		 * second child process into init process
		 */
		exit(0);
	}
}
